home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 19 / Mac Magazin and MacEasy Magazine CD - Issue 19.iso / Musik & Kunst / Ear Workout 2.1 / source code / ear_staff_notation.cp < prev    next >
Text File  |  1995-12-04  |  15KB  |  508 lines

  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4.  
  5. #include <OSUtils.h>
  6. #include <QuickDraw.h>
  7.  
  8. #define PI (3.141592653589793)
  9.  
  10. extern double sin(),cos(),fabs(),atan(),atan2(),sqrt(),exp(),log();
  11.  
  12.     void
  13. circle_through_three_points(double *x0,double *y0, double *r,
  14.             double *x,double *y);
  15.     void
  16. poly_to_arc_list(double *x0,double *y0,double *r,
  17.         double *start_theta,double *delta_theta,
  18.         double *x,double *y,int n,double scale,
  19.         int flip_y);
  20.     void
  21. draw_arc_list(int x,int y,double *x0,double *y0,double *r,
  22.         double *start_theta,double *delta_theta,
  23.         int n,double scale,double max_extra_angle);
  24.  
  25. //
  26. // problems:
  27. //    note heads look lousy with r>1 (I've set r=1 for now)
  28. //
  29.  
  30.     int
  31. draw_clef(
  32.     char *verb,        // "draw"
  33.     char *noun,        // "g_clef", "f_clef"
  34.     int x,            // horiz center of clef sign
  35.     int y,            // center line of staff
  36.     double h,        // dist between lines of staff
  37.     void *output_info,
  38.     void *style_info)
  39.     {
  40.  
  41.     // note that the y values are positive going _up_ on the screen:
  42.     #define N_G_CLEF_POINTS 21
  43.     static double g_clef_x[N_G_CLEF_POINTS] = {
  44.       1, -5,  1,  6,  0, -9,-11, -5, 2, 4, 4,
  45.        2,-1,-3,-4,-2,  1,  0,  -2,  -4, -6
  46.      };
  47.     static double g_clef_y[N_G_CLEF_POINTS] = { 
  48.     -15,-10, -2,-10,-20,-17, -5,  5,15,22,24,
  49.       29,28,23,20, 0,-20,-25,-29, -29,-25
  50.     };
  51.     #define N_F_CLEF_POINTS 10
  52.     static double f_clef_x[N_F_CLEF_POINTS] = {
  53.     -10, -1, 3, 2,-1,-5, -7,  -9,-8,-5
  54.     };
  55.     static double f_clef_y[N_F_CLEF_POINTS] = {
  56.     -12, -8, 0,18,19,18, 15,  13,11,10
  57.     };
  58.     
  59.     static double g_clef_x0[N_G_CLEF_POINTS],
  60.         g_clef_y0[N_G_CLEF_POINTS],
  61.         g_clef_r[N_G_CLEF_POINTS],
  62.         g_clef_start_theta[N_G_CLEF_POINTS],
  63.         g_clef_delta_theta[N_G_CLEF_POINTS];
  64.     static double f_clef_x0[N_F_CLEF_POINTS],
  65.         f_clef_y0[N_F_CLEF_POINTS],
  66.         f_clef_r[N_F_CLEF_POINTS],
  67.         f_clef_start_theta[N_F_CLEF_POINTS],
  68.         f_clef_delta_theta[N_F_CLEF_POINTS];
  69.     static int calculated_clefs = 0;
  70.  
  71.       if (!calculated_clefs) {
  72.         poly_to_arc_list(g_clef_x0,g_clef_y0,g_clef_r,
  73.         g_clef_start_theta,g_clef_delta_theta,
  74.         g_clef_x,g_clef_y,N_G_CLEF_POINTS,10.*h,1);
  75.         poly_to_arc_list(f_clef_x0,f_clef_y0,f_clef_r,
  76.         f_clef_start_theta,f_clef_delta_theta,
  77.         f_clef_x,f_clef_y,N_F_CLEF_POINTS,10.*h,1);
  78.         calculated_clefs = 1;
  79.       }
  80.  
  81.       if (strcmp(verb,"draw")==0) {
  82.         if (strcmp(noun,"g_clef")==0)
  83.           draw_arc_list(x,y,g_clef_x0,g_clef_y0,g_clef_r,
  84.         g_clef_start_theta,g_clef_delta_theta,
  85.         N_G_CLEF_POINTS,.01,20.);
  86.         if (strcmp(noun,"f_clef")==0) {
  87.           Rect rr;
  88.           short xx,yy,radius;
  89.           draw_arc_list(x,y,f_clef_x0,f_clef_y0,f_clef_r,
  90.         f_clef_start_theta,f_clef_delta_theta,
  91.         N_F_CLEF_POINTS,.01,20.);
  92.           radius = .15*h;
  93.           if (radius<1) radius=1;
  94.           xx = x+.8*h;
  95.           yy = y-1.5*h;
  96.           SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
  97.           FillOval(&rr,&black);
  98.           yy = y-0.5*h;
  99.           SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
  100.           FillOval(&rr,&black);
  101.             }
  102.       }
  103.       return 0;
  104.     }
  105.  
  106.     // Typically, max_extra_angle should be 20.  This
  107.     // controls how much extra it draws on the beginning
  108.     // and end of each arc, to try to make sure they connect
  109.     // up properly.  Kind of a kludge! Should improve this!
  110.     void
  111. draw_arc_list(int x,int y,double *x0,double *y0,double *r,
  112.         double *start_theta,double *delta_theta,
  113.         int n,double scale,double max_extra_angle)
  114.     {
  115.         int i,xx1,yy1,xx2,yy2;
  116.         short theta,d,zz;
  117.         Rect rr;
  118.         for (i=0; i<=n-3; i++) {
  119.           xx1 = x0[i]+r[i]*cos(PI/180.*start_theta[i]);
  120.           yy1 = y0[i]+r[i]*sin(PI/180.*start_theta[i]);
  121.           xx2 = x0[i]+r[i]*cos(PI/180.*(start_theta[i]+delta_theta[i]));
  122.           yy2 = y0[i]+r[i]*sin(PI/180.*(start_theta[i]+delta_theta[i]));
  123.           if (i>0)
  124.             LineTo((short) xx1,(short) yy1);
  125.                 // make sure we connect with previous arc
  126.           if (r[i]*scale<1000.) {
  127.               rr.left   = x+scale*(x0[i]-r[i]);
  128.               rr.right  = x+scale*(x0[i]+r[i]);
  129.               rr.top    = y+scale*(y0[i]-r[i]);
  130.               rr.bottom = y+scale*(y0[i]+r[i]);
  131.               
  132.               // make sure it doesn't leave gaps:
  133.               theta = start_theta[i];
  134.               zz = 10.*max_extra_angle/r[i];
  135.               if (zz>max_extra_angle) zz=max_extra_angle;
  136.               if (delta_theta[i]>0) {
  137.                 theta -= zz;
  138.                 d = delta_theta[i]+2*zz;
  139.               }
  140.               else {
  141.                 theta += zz;
  142.                 d = delta_theta[i]-2*zz;
  143.               }
  144.                 
  145.               FrameArc(&rr,theta,d);
  146.           }
  147.           else {
  148.             MoveTo((short) xx1,(short) yy1);
  149.             LineTo((short) xx2,(short) yy2);
  150.           }
  151.           MoveTo((short) xx2,(short) yy2); 
  152.              // prepare to connect up with beginning of next arc
  153.         }
  154.     }
  155.  
  156.     // This routine takes a list of points and figures out
  157.     // how to connect them smoothly with arcs.  The arc
  158.     // connecting points i and i+1 is chosen to be an arc
  159.     // of the circle passing through points i, i+1 and i+2.
  160.     // (The final arc is based on the last 3 points.)
  161.     // It's not as smart as it could be about choosing
  162.     // the direction of the arc.  To be safe, never try
  163.     // to make an arc of >=180 degrees.
  164.     // Everything is scaled up by a factor of scale.
  165.     // Should scale up quite a bit to avoid rounding errors
  166.     // in PtToAngle, but not so much that you'll overflow
  167.     // the short ints involved.
  168.     void
  169. poly_to_arc_list(double *x0,double *y0,double *r,
  170.         double *start_theta,double *delta_theta,
  171.         double *x,double *y,int n,double scale,
  172.         int flip_y)
  173.     {
  174.         int i,j;
  175.         Rect rr;
  176.         Point p;
  177.         short theta[3];
  178.         double s;
  179.         if (flip_y) s= -1.; else s=1;
  180.         for (i=0; i<=n-3; i++) {
  181.           double xx[3],yy[3];
  182.           // For maximum precision, scale _up_ by a factor of
  183.           // 10 now, then scale down later.  Invert y's at this
  184.           // point.
  185.           for (j=0; j<=2; j++) {
  186.             xx[j] =   scale*x[i+j];
  187.             yy[j] = s*scale*y[i+j];
  188.           }
  189.           circle_through_three_points(x0+i,y0+i,r+i,xx,yy);
  190.           
  191.           rr.left   = x0[i]-r[i];
  192.           rr.right  = x0[i]+r[i];
  193.           rr.top    = y0[i]-r[i];
  194.           rr.bottom = y0[i]+r[i];
  195.           
  196.           for (j=0; j<=2; j++) {
  197. #if 1
  198.             theta[j] = 90.-180./PI*atan2(-yy[j]+y0[i],xx[j]-x0[i]);
  199. #else
  200.             p.h = xx[j];
  201.             p.v = yy[j];
  202.             PtToAngle(&rr,p,theta+j);
  203. #endif
  204.           }
  205.           start_theta[i] = theta[0];
  206.           if (i<n-3)
  207.             delta_theta[i] = theta[1]-theta[0];
  208.           else
  209.             delta_theta[i] = theta[2]-theta[0];
  210.           while (delta_theta[i]>180.)
  211.             delta_theta[i] -= 360.;
  212.           while (delta_theta[i]<-180.)
  213.             delta_theta[i] += 360.;
  214.         }
  215.       
  216.     }
  217.  
  218.     // Style defined by:
  219.     //    h = distance between lines of staff = height of note heads
  220.     //    r = longest axis of note divided by h (>=1)
  221.     //    phi_deg = angle of tilt in degrees
  222.     // The only style item that must be explicitly set at the beginning
  223.     // is h; the rest have defaults.
  224.     //
  225.     // Although I currently draw note-heads as two semicircles connected
  226.     // by straight sides, there is nothing in the interface that forces
  227.     // the calling routine to know this or that would make it difficult
  228.     // to change the shape in the future.
  229.     //
  230.     // Returns 0 normally, nonzero on error.
  231.     // Error codes:
  232.     //    -1    never specified h
  233.     //    -2    unrecognized verb
  234.     int
  235. draw_note_head(
  236.          char *verb,    // "draw","left_tangent","right_tangent", or
  237.                      // "set_r","set_h","set_phi_deg"
  238.          int x,
  239.          int y,        // center of note in current coords
  240.          int if_filled,    // filled, like 1/4 note, or open, like 1/2?
  241.          void *output_info,    // Point * for tangents
  242.          void *style_info   // pointer to double for set_r, set_h, or set_phi_deg
  243.       )
  244.     {
  245.       static double r         = 1.;
  246.       static double h         = -1.; // no default for h
  247.       static double phi_deg   = 20.;
  248.       
  249.       static int recompute_style_stuff = 1;
  250.       static double phi,    // tilt angle, converted to radians
  251.               l,    // length of long axis
  252.               a,    // radius of circular ends
  253.               q,    // 2qa = length of straight sides
  254.               sin_phi,cos_phi,a_sin_phi,a_cos_phi,q_a_cos_phi,q_a_sin_phi;
  255.       
  256.       double xr,yr,xl,yl;
  257.       int verb_was_to_set_style;
  258.       
  259.  
  260.       verb_was_to_set_style = 0;
  261.       
  262.       if (strcmp(verb,"set_r")==0) {
  263.         r = * (double *) style_info;
  264.         recompute_style_stuff = 1;
  265.         verb_was_to_set_style = 1;
  266.       }
  267.       if (strcmp(verb,"set_h")==0) {
  268.         h = * (double *) style_info;
  269.         recompute_style_stuff = 1;
  270.         verb_was_to_set_style = 1;
  271.       }
  272.       if (strcmp(verb,"set_phi_deg")==0) {
  273.         phi_deg = * (double *) style_info;
  274.         recompute_style_stuff = 1;
  275.         verb_was_to_set_style = 1;
  276.       }
  277.       
  278.       if (h<0.) return -1;
  279.       
  280.       if (recompute_style_stuff) {
  281.         recompute_style_stuff = 0;
  282.         phi = phi_deg*PI/180.;
  283.         sin_phi = sin(phi);
  284.         cos_phi = cos(phi);
  285.         l = r*h;
  286.         q = (r-1.) / (1.-r*fabs(sin_phi));
  287.         a = .5*h/(1.+q*fabs(sin_phi));
  288.         a_sin_phi = a*sin_phi;
  289.         a_cos_phi = a*cos_phi;
  290.         q_a_cos_phi = q*a_cos_phi;
  291.         q_a_sin_phi = q*a_sin_phi;
  292.         
  293.       }
  294.       
  295.       if (verb_was_to_set_style) return 0;
  296.       
  297.       // center of right-hand semicircle:
  298.       xr = x + q_a_cos_phi;
  299.       yr = y - q_a_sin_phi;
  300.       
  301.       // center of left-hand semicircle:
  302.       xl = x - q_a_cos_phi;
  303.       yl = y + q_a_sin_phi;
  304.         
  305.       if (strcmp(verb,"right_tangent")==0) {
  306.         if (output_info != (void *) 0)
  307.           SetPt((Point *) output_info,(short) (xr+a+.5),(short) (yr+.5));
  308.         return 0;
  309.       }
  310.       if (strcmp(verb,"left_tangent")==0) {
  311.         if (output_info != (void *) 0)
  312.           SetPt((Point *) output_info,(short) (xl-a+.5),(short) (yl+.5));
  313.         return 0;
  314.       }
  315.       
  316. #if 1      
  317.       if (strcmp(verb,"draw")==0) {
  318.         Rect r;
  319.         RgnHandle region1,region2,region3,whole_region;
  320.         PolyHandle p;
  321.         
  322.         region1 = NewRgn();
  323.         OpenRgn();
  324.         SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
  325.                 (short) (xr+a+.5),(short) (yr+a+.5));
  326.         FrameOval(&r);
  327.         CloseRgn(region1);
  328.         
  329.         region2 = NewRgn();
  330.         OpenRgn();
  331.         p = OpenPoly();
  332.         #define ZOT 0.5
  333.         //-- flat top part:
  334.         MoveTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
  335.                (short) (y-a_cos_phi-q_a_sin_phi+ZOT));
  336.         LineTo((short) (x-a_sin_phi-q_a_cos_phi+ZOT),
  337.                (short) (y-a_cos_phi+q_a_sin_phi+ZOT));
  338.         //-- flat bottom part:
  339.         LineTo((short) (x+a_sin_phi-q_a_cos_phi+ZOT),
  340.                (short) (y+a_cos_phi+q_a_sin_phi+ZOT));
  341.         LineTo((short) (x+a_sin_phi+q_a_cos_phi+ZOT),
  342.                (short) (y+a_cos_phi-q_a_sin_phi+ZOT));
  343.         //-- back to start:
  344.         LineTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
  345.                (short) (y-a_cos_phi-q_a_sin_phi+ZOT));
  346.         ClosePoly();
  347.         FramePoly(p);
  348.         CloseRgn(region2);
  349.  
  350.         region3 = NewRgn();
  351.         OpenRgn();
  352.         SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
  353.                 (short) (xl+a+.5),(short) (yl+a+.5));
  354.         FrameOval(&r);
  355.         CloseRgn(region3);
  356.         
  357.         whole_region = NewRgn();
  358.         UnionRgn(region1,region2,whole_region);
  359.         UnionRgn(whole_region,region3,whole_region);
  360.         
  361.         //--- finally: draw it!
  362.         if (if_filled)
  363.           FillRgn(whole_region,&black);
  364.         else
  365.           FrameRgn(whole_region); 
  366.         
  367.         DisposeRgn(region1);
  368.         DisposeRgn(region2);
  369.         DisposeRgn(region3);
  370.         DisposeRgn(whole_region);
  371.         KillPoly(p);    //deallocate memory
  372.         
  373.         return 0;
  374.       }
  375. #endif
  376. #if 0      
  377.       if (strcmp(verb,"draw")==0) {
  378.         Rect r;
  379.         SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
  380.                 (short) (xr+a+.5),(short) (yr+a+.5));
  381.         if (if_filled)
  382.           FillArc(&r,(short) (180-phi_deg+.5),(short) -180,&black);
  383.         else
  384.           FrameArc(&r,(short) (180-phi_deg+.5),(short) -180);
  385.         if (if_filled) {
  386.           PolyHandle p;
  387.           p = OpenPoly();
  388.           MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
  389.                (short) (y-a_cos_phi-q_a_sin_phi+.5));
  390.           LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
  391.                (short) (y-a_cos_phi+q_a_sin_phi+.5));
  392.           LineTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
  393.                (short) (y+a_cos_phi+q_a_sin_phi+.5));
  394.           LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
  395.                (short) (y+a_cos_phi-q_a_sin_phi+.5));
  396.           LineTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
  397.                (short) (y-a_cos_phi-q_a_sin_phi+.5));
  398.           ClosePoly();
  399.           FillPoly(p,&black);
  400.           KillPoly(p);
  401.         }
  402.         else {
  403.           MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
  404.                (short) (y-a_cos_phi-q_a_sin_phi+.5));
  405.           LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
  406.                (short) (y-a_cos_phi+q_a_sin_phi+.5));
  407.           MoveTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
  408.                (short) (y+a_cos_phi+q_a_sin_phi+.5));
  409.           LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
  410.                (short) (y+a_cos_phi-q_a_sin_phi+.5));
  411.         }
  412.         SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
  413.                 (short) (xl+a+.5),(short) (yl+a+.5));
  414.         if (if_filled)
  415.           FillArc(&r,(short) (360-phi_deg+.5),(short) -180,&black);
  416.         else
  417.           FrameArc(&r,(short) (360-phi_deg+.5),(short) -180);
  418.         return 0;
  419.       }
  420. #endif
  421.  
  422.       return -2;
  423.  
  424.     }
  425.  
  426.     void
  427. draw_staff(Rect *rec)
  428.     {
  429.       short t,b,l,r,y;
  430.       double h;
  431.       int i;
  432.       t = rec->top;
  433.       b = rec->bottom;
  434.       l = rec->left;
  435.       r = rec->right;
  436.       h = b-t;
  437.       h = h/4.;
  438.       for (i=0; i<=4; i++) {
  439.         y = t+h*i+.5;
  440.         MoveTo(l,y);
  441.         LineTo(r,y);
  442.       }
  443.     }
  444.  
  445.     void
  446. circle_through_three_points(double *x0,double *y0, double *r,
  447.             double *x,double *y)
  448.     {
  449.       double xm1,ym1,xm2,ym2,a1,a2,b1,b2,lx,ly,typical_scale,z;
  450.       
  451.       // If either line seg is horizontal, rotate everything
  452.       //    by 20 degrees and do recursion:
  453.       if (fabs(y[0]-y[1])<1e-6 || fabs(y[1]-y[2])<1e-6) {
  454.         double xx[3],yy[3];
  455.         int i;
  456.         for (i=0; i<=2; i++) {
  457.           xx[i] = x[i]*0.939693 - y[i]*0.342020;
  458.           yy[i] = x[i]*0.342020 + y[i]*0.939693;
  459.         }
  460.         circle_through_three_points(x0,y0,r,xx,yy);
  461.         *x0 =  (*x0)*0.939693 + (*y0)*0.342020;
  462.         *y0 = -(*x0)*0.342020 + (*y0)*0.939693;
  463.         return;
  464.       }
  465.       
  466.       // find midpoints of two line segs:
  467.       xm1 = .5*(x[0]+x[1]);
  468.       ym1 = .5*(y[0]+y[1]);
  469.       xm2 = .5*(x[1]+x[2]);
  470.       ym2 = .5*(y[1]+y[2]);
  471.       
  472.       typical_scale = fabs(xm1-xm2)+fabs(ym1-ym2);
  473.       
  474.       // find slopes of normals to line segs; guaranteed
  475.       //   not to have any problems with overflows, because
  476.       //   we already made sure nothing was exactly horizontal
  477.       a1 = -(x[1]-x[0])/(y[1]-y[0]);
  478.       a2 = -(x[2]-x[1])/(y[2]-y[1]);
  479.       
  480.       // put normals in the form y=ax+b
  481.       b1 = ym1-a1*xm1;
  482.       b2 = ym2-a2*xm2;
  483.       
  484.       // Solve eqns simultaneously to get intersection.
  485.       // If slopes are the same, put the intersection way
  486.       // off to one side.
  487.       if (fabs(a1-a2)==0.) {
  488.         // First try:
  489.         *x0 = .5*(xm1+xm2)+1e6*typical_scale;
  490.         *y0 = a1*(*x0)+b1;
  491.         // Second try: now make sure it about as far off as we
  492.         // wanted it:
  493.         z = fabs(*x0-.5*(xm1+xm2))+fabs(*y0-.5*(ym1+ym2));
  494.         *x0 = .5*(xm1+xm2)
  495.                 +(*(x0)-.5*(xm1+xm2))*1e6*typical_scale/z;
  496.         *y0 = a1*(*x0)+b1;
  497.       }
  498.       else {
  499.         *x0 = (b1-b2)/(a2-a1);
  500.         *y0 = a1*(*x0)+b1;
  501.       }
  502.             
  503.       lx = x[0] - *x0;
  504.       ly = y[0] - *y0;
  505.       *r = sqrt(lx*lx+ly*ly);
  506.     }
  507.  
  508.